#pragma once

typedef long64 (*ZFILEREAD)(void*, ulong64, ulong64, struct zfile*);
typedef long64 (*ZFILEWRITE)(void*, ulong64, ulong64, struct zfile*);
typedef long64 (*ZFILESEEK)(struct zfile*, long64, int);

struct zfile
{
    TCHAR* name;
    TCHAR* zipname;
    TCHAR* mode;
    FILE* f; // real file handle if physical file
    byte* data; // unpacked data
    int dataseek; // use seek position even if real file
    struct zfile* archiveparent; // set if parent is archive and this has not yet been unpacked (datasize < size)
    int archiveid;
    long64 size; // real size
    long64 datasize; // available size (not yet unpacked completely?)
    long64 allocsize; // memory allocated before realloc() needed again
    long64 seek; // seek position
    int deleteafterclose;
    int textmode;
    struct zfile* next;
    int zfdmask;
    struct zfile* parent;
    ulong64 offset; // byte offset from parent file
    int opencnt;
    ZFILEREAD zfileread;
    ZFILEWRITE zfilewrite;
    ZFILESEEK zfileseek;
    void* userdata;
    int useparent;
};

#define ZNODE_FILE 0
#define ZNODE_DIR 1
#define ZNODE_VDIR -1
struct znode
{
    int type;
    struct znode* sibling;
    struct znode* child;
    struct zvolume* vchild;
    struct znode* parent;
    struct zvolume* volume;
    struct znode* next;
    struct znode* prev;
    struct znode* vfile; // points to real file when this node is virtual directory
    TCHAR* name;
    TCHAR* fullname;
    long64 size;
    struct zfile* f;
    TCHAR* comment;
    int flags;
    time_t mtime;
    /* decompressor specific */
    uint offset;
    uint offset2;
    uint method;
    uint packedsize;
};

struct zvolume
{
    struct zfile* archive;
    void* handle;
    struct znode root;
    struct zvolume* next;
    struct znode* last;
    struct znode* parentz;
    struct zvolume* parent;
    long64 size;
    uint blocks;
    uint id;
    long64 archivesize;
    uint method;
    TCHAR* volumename;
    int zfdmask;
};

struct zarchive_info
{
    TCHAR* name;
    long64 size;
    int flags;
    TCHAR* comment;
    time_t t;
};

#define ArchiveFormat7Zip '7z  '
#define ArchiveFormatRAR 'rar '
#define ArchiveFormatZIP 'zip '
#define ArchiveFormatLHA 'lha '
#define ArchiveFormatLZX 'lzx '
#define ArchiveFormatPLAIN '----'
#define ArchiveFormatDIR 'DIR '
#define ArchiveFormatAA 'aa  ' // method only
#define ArchiveFormatADF 'DOS '
#define ArchiveFormatRDB 'RDSK'
#define ArchiveFormatMBR 'MBR '
#define ArchiveFormatFAT 'FAT '
#define ArchiveFormatTAR 'tar '

#define PEEK_BYTES 1024
#define FILE_PEEK 1
#define FILE_DELAYEDOPEN 2

extern int zfile_is_ignore_ext(const TCHAR* name);

extern struct zvolume* zvolume_alloc(struct zfile* z, uint id, void* handle, const TCHAR*);
extern struct zvolume* zvolume_alloc_empty(struct zvolume* zv, const TCHAR* name);

extern struct znode* zvolume_addfile_abs(struct zvolume* zv, struct zarchive_info*);
extern struct znode* zvolume_adddir_abs(struct zvolume* zv, struct zarchive_info* zai);
extern struct znode* znode_adddir(struct znode* parent, const TCHAR* name, struct zarchive_info*);

extern struct zvolume* archive_directory_plain(struct zfile* zf);
extern struct zfile* archive_access_plain(struct znode* zn);
extern struct zvolume* archive_directory_lha(struct zfile* zf);
extern struct zfile* archive_access_lha(struct znode* zn);
extern struct zvolume* archive_directory_zip(struct zfile* zf);
extern struct zfile* archive_access_zip(struct znode* zn, int flags);
extern struct zvolume* archive_directory_7z(struct zfile* z);
extern struct zfile* archive_access_7z(struct znode* zn);
extern struct zvolume* archive_directory_rar(struct zfile* z);
extern struct zfile* archive_access_rar(struct znode* zn);
extern struct zvolume* archive_directory_lzx(struct zfile* in_file);
extern struct zfile* archive_access_lzx(struct znode* zn);
extern struct zvolume* archive_directory_arcacc(struct zfile* z, uint id);
extern struct zfile* archive_access_arcacc(struct znode* zn);
extern struct zvolume* archive_directory_adf(struct znode* zn, struct zfile* z);
extern struct zfile* archive_access_adf(struct znode* zn);
extern struct zvolume* archive_directory_rdb(struct zfile* z);
extern struct zfile* archive_access_rdb(struct znode* zn);
extern struct zvolume* archive_directory_fat(struct zfile* z);
extern struct zfile* archive_access_fat(struct znode* zn);
extern struct zfile* archive_access_dir(struct znode* zn);
extern struct zvolume* archive_directory_tar(struct zfile* zf);
extern struct zfile* archive_access_tar(struct znode* zn);

extern struct zfile* archive_access_select(struct znode* parent, struct zfile* zf, uint id, int doselect, int* retcode, int index);
extern struct zfile* archive_access_arcacc_select(struct zfile* zf, uint id, int* retcode);
extern int isfat(byte*);

extern void archive_access_scan(struct zfile* zf, zfile_callback zc, void* user, uint id);

extern void archive_access_close(void* handle, uint id);

extern struct zfile* archive_getzfile(struct znode* zn, uint id, int flags);
extern struct zfile* archive_unpackzfile(struct zfile* zf);

extern struct zfile* decompress_zfd(struct zfile*);